import numpy as np
from medpy import metric
class Metrics:
    def __init__(self):
        pass

    def __call__(self, pred, target):
        # 其中一个类的dice iou hd95
        # pred = pred.astype(int)
        # target = pred.astype(int)

        # print("pred.unique", np.unique(pred))
        # print("target.unique", np.unique(target))
        # print("pred,target .shape".format(pred.shape, target.shape))
        # print("type",type(pred),type(target))
        # pred[pred > 0] = 1
        #         # target[target > 0] = 1
        # print("pred.unique", np.unique(pred))
        # print("target.unique", np.unique(target))
        # print("pred,target .shape".format(pred.shape, target.shape))
        return self.dice_val(pred,target),self.iou_val(pred,target),self.hd95_val(pred,target)

    def dice_val(self,pred, target):
        alpha = 1e-6
        # print("11 pred", np.unique(pred))
        # print("11 pred.sum", pred.sum())

        pred = (pred != 0).astype(np.float32)
        target = (target != 0).astype(np.float32)
        # print("11 pred", np.unique(pred))
        # print("11 pred.sum", pred.sum())

        inter = np.sum((target + pred) == 2)
        #???? alpha防止除以0
        return float(2 * inter) / (float(np.sum(pred) + np.sum(target)) + alpha)

    def iou_val(self,pred, target):

        alpha = 1e-6
        pred = (pred != 0).astype(np.float32)
        target = (target != 0).astype(np.float32)

        TP = (pred + target == 2).astype(np.float32)
        FP = (pred + (1 - target) == 2).astype(np.float32)
        FN = ((1 - pred) + target == 2).astype(np.float32)
        return float(np.sum(TP)) / (float(np.sum(TP + FP + FN)) + alpha)

    def hd95_val(self,pred,target):
        # print("11 pred={0} target={1}".format(np.unique(pred), np.unique(target)))
        # print("11 pred.sum={0} target={1}".format(pred.sum(), target.sum()))
        if pred.sum() > 0 and target.sum() > 0:

            pred = (pred != 0).astype(np.float32)
            target = (target != 0).astype(np.float32)
            # print("22 pred={0} target={1}".format(np.unique(pred), np.unique(target)))
            # print("22 pred.sum{0} target={1}".format(pred.sum(),target.sum()))

            hd95 = metric.binary.hd95(pred, target)
        else :
            hd95 = 0.0
        return hd95

class DSC:
    """
    Dice Similarity Coefficient
    """
    def __init__(self, name='DSC', alpha=1e-6) -> None:
        super().__init__()
        self.name = name
        self.alpha = alpha

    def __call__(self, pred, target):
        pred = (pred != 0).astype(np.float32)
        target = (target != 0).astype(np.float32)

        inter = np.sum((target + pred) == 2)
        #???? alpha防止除以0
        return float(2 * inter) / (float(np.sum(pred) + np.sum(target)) + self.alpha)


class IOU:

    def __init__(self, name='IOU', alpha=1e-6) -> None:
        super().__init__()
        self.name = name
        self.alpha = alpha

    def __call__(self, pred, target):
        pred = (pred != 0).astype(np.float32)
        target = (target != 0).astype(np.float32)
        TP = (pred + target == 2).astype(np.float32)
        FP = (pred + (1 - target) == 2).astype(np.float32)
        FN = ((1 - pred) + target == 2).astype(np.float32)
        return float(np.sum(TP)) / (float(np.sum(TP + FP + FN)) + self.alpha)


def dice_val_ext(pred, target):
    alpha = 1e-6

    pred = (pred != 0).astype(np.float32)
    target = (target != 0).astype(np.float32)

    inter = np.sum((target + pred) == 2)
    #???? alpha防止除以0
    return float(2 * inter) / (float(np.sum(pred) + np.sum(target)) + alpha)

def iou_val_ext(pred, target):
    alpha = 1e-6

    pred = (pred != 0).astype(np.float32)
    target = (target != 0).astype(np.float32)


    TP = (pred + target == 2).astype(np.float32)
    FP = (pred + (1 - target) == 2).astype(np.float32)
    FN = ((1 - pred) + target == 2).astype(np.float32)
    return float(np.sum(TP)) / (float(np.sum(TP + FP + FN)) + alpha)

def hd95_val_ext(pred,target):

    if pred.sum()>0:
        pred = (pred != 0).astype(np.float32)
        target = (target != 0).astype(np.float32)
        hd95 = metric.binary.hd95(pred, target)
    else :
        hd95 = 0.0
    return hd95
